////////////////////////////////////////////////////////////////////////////////
//
// labFisRisonanza.cc - acquire data for SNS Lab Fis resonance experiment
// 
// 1st release: A. Lusiani <a.lusiani@sns.it> April 2003
//
// this program includes parts from poll.c, Part of Comedilib,
// Copyright (c) 1999,2000 David A. Schleef <ds@schleef.org>
// poll.c may be freely modified, distributed, and combined with
// other software, as long as proper attribution is given in the
// source code.
//

////////////////////////////////////////////////////////////////////////////////
//
// Modifications:
//
//

////////////////////////////////////////////////////////////////////////////////
//
// headers
//

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <comedilib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <sys/select.h>
}

#ifndef __CINT__
#include "TFile.h"
#include "TApplication.h"
#include "TCanvas.h"
#include "TH1.h"
#include "TH2.h"
#include "TProfile.h"
#include "TNtuple.h"
#include "TRandom.h"
#endif

#include "labFisDaq.h"

////////////////////////////////////////////////////////////////////////////////
//
// function declarations
//

////////////////////////////////////////////////////////////////////////////////
//
// variable  declarations
//

const char* 		COMEDI_DEV = "/dev/comedi0";
int			AI_SUBDEV;
int			AO_SUBDEV;
//--- comedi sub-devices for Digital Output
int			DO_SUBDEV;

//--- buffer size for streaming data out of the driver
const unsigned int	COMEDI_BUFSIZ =	(4*1024*1024);

//--- Analog Input definitions
const unsigned int 	WAVEGEN_AI_CHAN=0;
const unsigned int 	MICEND_AI_CHAN=1;
const unsigned int 	MICMID_AI_CHAN=2;
const unsigned int 	TEMP1_AI_CHAN=3;
const unsigned int 	TEMP2_AI_CHAN=4;
const unsigned int 	TEMP3_AI_CHAN=5;
const unsigned int 	TEMP4_AI_CHAN=6;

//--- definitions to describe input channels for asynchronous DAQ
#define AISCAN_BEG	(0)
#define AISCAN_NUM	(3)
unsigned int aiScanList[AISCAN_NUM];

//--- Analog Input voltage range
const float 		AI_VMIN = -10;
const float 		AI_VMAX = 10;

//--- select analog input voltage reference from GROUND
const unsigned int	AI_AREF = AREF_GROUND;

//--- rate at which the input is sampled, 100 kHz
unsigned int 		AISCAN_RATE = 10000;
#define 		AISCAN_SAMPLES (1000)

//--- Analog Out definition
const unsigned int 	FREQTUNE_AO_CHAN = 0;

//--- minimum Analog Output value for smallest frequency
double 			FREQTUNE_MIN = -0.4;
//--- maximum Analog Output value for largest frequency
double			FREQTUNE_MAX = 0.4;
//--- number of frequency steps minus one
const unsigned int	FREQTUNE_NUM = 20;

//--- select Analog Output voltage reference from GROUND
const unsigned int	AO_AREF = AREF_GROUND;

////////////////////////////////////////////////////////////////////////////////
//
// code
//

int main(int argc, char *argv[])
{
  int i;
  int rc;
  comedi_t *cifh;
  comedi_cmd cmd;
  lsampl_t maxDataAi;
  lsampl_t maxDataAo;
  double minVoltsAi, maxVoltsAi;
  double minVoltsAo, maxVoltsAo;
  unsigned int rangetAi;
  comedi_range* rangepAi;
  unsigned int rangetAo;
  comedi_range* rangepAo;
  //--- buffer for ni_pcimio Analog Input driver (16-bit unsigned samples)
  sampl_t buf[AISCAN_SAMPLES*AISCAN_NUM];
  
  //--- endpoints of acquisition are valid numbers
  rc = comedi_set_global_oor_behavior(COMEDI_OOR_NUMBER);

  cifh = comedi_open(COMEDI_DEV);
  if(!cifh){
    perror(COMEDI_DEV);
    exit(1);
  }

  //--- set comedi device streaming buffer size
  rc = comedi_set_buffer_size(cifh, AI_SUBDEV, COMEDI_BUFSIZ);
  if (rc<0) {
    fprintf(stderr, "comedi_set_buffer_size: %s\n",strerror(errno));
    exit(1);
  } else {
    rc = comedi_get_buffer_size(cifh, AI_SUBDEV);
    if (rc<0) {
      perror("comedi_get_buffer_size");
    } else {
      fprintf(stderr, "comedi buffer size for AI set to 0x%x bytes\n", rc);
    }
  }

  //--- find which subdevice offers Analog Input
  rc = comedi_find_subdevice_by_type(cifh, COMEDI_SUBD_AI, 0);
  if (rc<0){
    comedi_perror("comedi_find_device AI");
    exit(1);
  }
  AI_SUBDEV = rc;

  //--- find which subdevice offers Analog Output
  rc = comedi_find_subdevice_by_type(cifh, COMEDI_SUBD_AO, 0);
  if (rc<0){
    comedi_perror("comedi_find_device AO");
    exit(1);
  }
  AO_SUBDEV = rc;

  //--- find which subdevice offers Digital Output
  rc = comedi_find_subdevice_by_type(cifh, COMEDI_SUBD_DO,0);
  if (rc<0){
    rc = comedi_find_subdevice_by_type(cifh, COMEDI_SUBD_DIO, 0);
    if (rc<0){
      comedi_perror("comedi_find_device DO and DIO");
      exit(1);
    }
    DO_SUBDEV = rc;
    rc = comedi_dio_config(cifh, rc, 0, COMEDI_OUTPUT);
    if (rc<0){
      comedi_perror("comedi_dio_config");
      exit(1);
    }
  } else {
    DO_SUBDEV = rc;
  }
  fprintf(stderr, "Using Digital Output subdevice %d\n", DO_SUBDEV);

  //--- find AI range for -10 Volt .. 10 Volt
  rc = comedi_find_range(cifh, AI_SUBDEV,
			 WAVEGEN_AI_CHAN, UNIT_volt, AI_VMIN, AI_VMAX);
  if (rc<0){
    comedi_perror("comedi_find_range");
    exit(1);
  }
  rangetAi = rc;

  //--- get ptr to range struct
  rangepAi = comedi_get_range(cifh, AI_SUBDEV, WAVEGEN_AI_CHAN, rangetAi);
  //--- get maximum possible sample for AI
  maxDataAi = comedi_get_maxdata(cifh, AI_SUBDEV, WAVEGEN_AI_CHAN);
  minVoltsAi = comedi_to_phys(0, rangepAi, maxDataAi);
  maxVoltsAi = comedi_to_phys(maxDataAi, rangepAi, maxDataAi);

  fprintf(stderr, "Comedi offers AI range n. %d, %f .. %f\n",
	 rangetAi, minVoltsAi, maxVoltsAi);

  //--- build channel list for DAQ scan of microphone
  for(i=0;i<AISCAN_NUM;i++){
    aiScanList[i] = CR_PACK(AISCAN_BEG+i, rangetAi, AI_AREF);
  }

  //--- find AO range for -10 Volt .. 10 Volt
  rc = comedi_find_range(cifh, AO_SUBDEV,
			 FREQTUNE_AO_CHAN, UNIT_volt,
			 FREQTUNE_MIN, FREQTUNE_MAX);
  if (rc<0){
    comedi_perror("comedi_find_range");
    exit(1);
  }
  rangetAo = rc;

  //--- get ptr to range struct
  rangepAo = comedi_get_range(cifh, AO_SUBDEV, FREQTUNE_AO_CHAN, rangetAo);
  //--- get maximum possible sample for AO
  maxDataAo = comedi_get_maxdata(cifh, AO_SUBDEV, FREQTUNE_AO_CHAN);
  minVoltsAo = comedi_to_phys(0, rangepAo, maxDataAo);
  maxVoltsAo = comedi_to_phys(maxDataAo, rangepAo, maxDataAo);

  fprintf(stderr, "Comedi offers AO range n. %d, %f .. %f\n",
	 rangetAo, minVoltsAo, maxVoltsAo);

  //--- prepare AI scan for microphone's input
  labFisScanPrepare(cifh, AI_SUBDEV, &cmd,
		    TRIG_NOW, AISCAN_RATE, TRIG_COUNT, AISCAN_SAMPLES,
		    aiScanList, AISCAN_NUM);
  
  //
  // initialize Root
  //
  // code inspired from:
  // - test/hsimple.cxx of Root 3.05.3 package
  //

  // Create a new ROOT binary machine independent file.
  // This file is now becoming the current directory.
  TFile hfile("labFisRisonanza.root", "RECREATE",
	      "Lab Fis: risonanza di onde acustiche");

  //--- allocate Root Tree
  TTree *tree = new TTree("nt", "n-pla per risonanza di onde acustiche");

  //--- Root ntuple definition: see Root/tutorials/cernbuild.C

  //--- allocate an unsigned integer variable
  Float_t ntWg;
  Float_t ntEnd;
  Float_t ntMid;
  //--- add to the "tree" ntuple 3 branches, each with a single leaf
  tree->Branch("wg",&ntWg,"wg/F");
  tree->Branch("end",&ntEnd,"end/F");
  tree->Branch("mid",&ntMid,"mid/F");

  TH1F *hsMicro = new TH1F("hsMicro", "Audio signal distribution",
			   100, -10, 10);
  hsMicro->SetXTitle("Volts");
  hsMicro->SetYTitle("events");

  TProfile *hsWavegen =
    new TProfile("hsWavegen", "Wave generator signal vs. time",
		 AISCAN_SAMPLES, -0.5/AISCAN_RATE,
		 (static_cast<double>(AISCAN_SAMPLES)-0.5)/AISCAN_RATE,
		 -10, 10);
  hsWavegen->SetXTitle("seconds");
  hsWavegen->SetYTitle("Volts");

  TProfile *hsMicEnd =
    new TProfile("hsMicEnd", "End micro signal vs. time",
		 AISCAN_SAMPLES, -0.5/AISCAN_RATE,
		 (static_cast<double>(AISCAN_SAMPLES)-0.5)/AISCAN_RATE,
		 -10, 10);
  hsMicEnd->SetXTitle("seconds");
  hsMicEnd->SetYTitle("Volts");

  TProfile *hsMicMid =
    new TProfile("hsMicMid", "Mid micro signal vs. time",
		 AISCAN_SAMPLES, -0.5/AISCAN_RATE,
		 (static_cast<double>(AISCAN_SAMPLES)-0.5)/AISCAN_RATE,
		 -10, 10);
  hsMicMid->SetXTitle("seconds");
  hsMicMid->SetYTitle("Volts");

  //--- ask for non-blocking I/O on comedi
  fcntl(comedi_fileno(cifh),F_SETFL,O_NONBLOCK);

  //--- allocate object to setup terminal input
  labFisTermIoHandler tioh(stdin);
  //--- setup terminal for immediate keypress input with no echo
  tioh.SetImmedNoecho();

  //
  // read temperatures
  //
  for(int ii = 0; i<20; i++) {
  fprintf(stderr,"Temperatures:");
  for(unsigned int iChan=TEMP1_AI_CHAN; iChan<=TEMP4_AI_CHAN; iChan++) {
    lsampl_t data;
    
    //--- two channels are missing
    if (iChan == TEMP2_AI_CHAN) continue;
    if (iChan == TEMP3_AI_CHAN) continue;
    
    rc = comedi_data_read(cifh, AI_SUBDEV, iChan, rangetAi, AI_AREF, &data);
    if (rc<0){
      comedi_perror("comedi_read_data");
      exit(1);
    }
    fprintf(stderr," %8.4f", comedi_to_phys(data, rangepAi, maxDataAi));
  }
  fprintf(stderr,"\n");
  usleep(200000);
  }

  //
  // acquire microphones data for a number of frequencies around resonance
  //
  for (unsigned int iFreq = 0; iFreq <= FREQTUNE_NUM; iFreq++) {
    lsampl_t data = comedi_from_phys(FREQTUNE_MIN+
				     ((double)iFreq)/FREQTUNE_NUM*
				     (FREQTUNE_MAX-FREQTUNE_MIN),
				     rangepAo, maxDataAo);
    fprintf(stderr,
	    "Preparing scan for frequency step %3d (0x%04x, %4d)\n",
	    iFreq, data, data-0x8000);
    rc = comedi_data_write(cifh, AO_SUBDEV, FREQTUNE_AO_CHAN,
			   rangetAo, AO_AREF, data);
    if (rc<0){
      comedi_perror("comedi_write_data");
      exit(1);
    }

    //--- wait # nanoseconds for letting signals settle down 
    usleep(1000000);
    system("echo -n \007");
    
    //--- start comedi Analog Input scan
    rc = comedi_command(cifh, &cmd);
    fprintf(stderr,"Start DAQ, rc= %d\n", rc);
    if (rc<0) {
      fprintf(stderr,"errno=%d\n",errno);
      comedi_perror("comedi_command");
      exit(1);
    }
    
    //--- init sample number
    unsigned int scansTot = 0;
    
    //--- DAQ loop (endless)
    while (1) {
      //--- read available data
      rc = labFisScanRead(cifh, AI_SUBDEV, &buf[0], sizeof(buf),
			  static_cast<long int>(1e6), 10);
      
      //--- end endless loop if end of data reached
      if (rc == 0) {
	fprintf(stderr, "EOF, ending scan after reading %6d samples\n",
		scansTot);
	break;
      }
      
      //--- read available data
      if (rc > 0) {
	unsigned int i;
	
	unsigned int len = rc/sizeof(sampl_t);
	for(i=0; i < len; i++, scansTot++) {
	  
	  unsigned int scanNum = scansTot / AISCAN_NUM;

	  switch(scansTot % AISCAN_NUM) {
	  case 0:
	    //--- modulo #0 samples: wave generator
	    //--- fill histogram of read voltages vs. time in seconds
	    ntWg = comedi_to_phys(buf[i], rangepAi, maxDataAi);
	    if (iFreq == FREQTUNE_NUM/2) {
	      //--- fill histogram only for central freq.
	      hsWavegen->Fill(static_cast<double>(scanNum)/AISCAN_RATE, ntWg);
	    }
	    break;
	  case 1:
	    //--- modulo #1 samples: end-cap micro
	    //--- fill histogram of read voltages vs. time in seconds
	    ntEnd = comedi_to_phys(buf[i], rangepAi, maxDataAi);
	    if (iFreq == FREQTUNE_NUM/2) {
	      //--- fill histogram only for central freq.
	      hsMicEnd->Fill(static_cast<double>(scanNum)/AISCAN_RATE, ntEnd);
	    }
	    break;
	  case 2:
	    //--- modulo #2 samples: median micro
	    ntMid = comedi_to_phys(buf[i], rangepAi, maxDataAi);
	    if (iFreq == FREQTUNE_NUM/2) {
	      //--- fill histogram only for central freq.
	      hsMicMid->Fill(static_cast<double>(scanNum)/AISCAN_RATE, ntMid);
	    }
	    //--- store this event ntuple after setting last DAQ variable
	    tree->Fill();
	    break;
	  }

	  //--- fill histogram of read voltages (from all sources)
	  hsMicro->Fill(comedi_to_phys(buf[i], rangepAi, maxDataAi));

	}
	continue;
      }
      
      if (-rc == LFDAQ_STDINRDY) {
	unsigned char ch = fgetc(stdin);
	if (ch == 25) {
	  fprintf(stderr,"  --- <control-Y> received, quitting ... --- \n");
	  break;
	} else {
	  fprintf(stderr,"  --- Press <control-Y> to quit --- \n");
	}
	continue;
      }
      
      if (-rc == LFDAQ_TIMEOUT) {
	fprintf(stderr,"Timeout waiting for data \n");
	break;
      }
      
      break;
    }
  }

  //--- leave frequency tuning DAC value at 0 Volts
  lsampl_t data = comedi_from_phys(0, rangepAo, maxDataAo);
  fprintf(stderr, "Leaving frequency at central value (0x%04x, %4d)\n",
	  data, data-0x8000);
  rc = comedi_data_write(cifh, AO_SUBDEV, FREQTUNE_AO_CHAN,
			 rangetAo, AO_AREF, data);
  if (rc<0){
    comedi_perror("comedi_write_data");
    exit(1);
  }

  //--- Save all objects in this file
  hfile.Write();

  //--- Close the file.
  hfile.Close();

  //--- open saved file for reading
  TFile hfileR("labFisRisonanza.root", "READ");

  //--- recover a pointer to the histogram "hsMicroTimeProf"
  TH1D *hs = (TH1D *)hfileR.Get("hsMicEnd");
  hs->SetMaximum(10);
  hs->SetMinimum(-10);

  //--- init an object to get Root GUI on a canvas
  TApplication theApp("App", &argc, argv);

  //--- init an object to get a graphical canvas
  TCanvas *canvas = new TCanvas("canvas", "Microphone vs. time", 600, 300);

  //--- draw the histogram
  hs->Draw("hist C");

  //--- get the canvas updated
  canvas->Update();

  //--- recover pointer to Root tree (ntuple) "nt"
  TTree *treeR = (TTree *)hfileR.Get("nt");

  //--- start root GUI: "Exit ROOT" will return to the next statement
  theApp.Run(kTRUE);

  //--- Close the file.
  hfile.Close();

  return 0;
}
